//
// base64.cpp
// libprotea
// Tools Library for the Protea Project / BASE64 encoding/decoding algorithm
// Copyright (C) 2003, 2004, 2005 eXegeSys, Inc.
// Copyright (C) 2008 Bruce A. James
//
// This program is part of the Protea Project.
//
// The Protea Project is free software; you can redistribute it and/or modify it 
// under the terms of the GNU General Public License as published by the 
// Free Software Foundation, either version 3 of the License, or (at your 
// option) any later version.
//
// The Protea Project is distributed in the hope that it will be useful, but 
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
// for more details.
// 
// You should have received a copy of the GNU General Public License along 
// with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// Please send correspondence to:
// 
// theproteaproject@gmail.com
//

// base64.cpp

#include "../defines.h"
#include "base64.h"
#include <stdio.h>

char XCBase64::Encode( unsigned char Byte ) {
	// 0 - 25    ->   'A' - 'Z'
	// 26 - 51   ->   'a' - 'z'
	// 52 - 61   ->   '0' - '9'
	// 62        ->   '+'
	// 63        ->   '/'
	if (Byte < 26) return 'A' + Byte;
	else if (Byte < 52) return 'a' + (Byte - 26);
	else if (Byte < 62) return '0' + (Byte - 52);
	else if (Byte == 62) return '+';
	return '/';
}

unsigned char XCBase64::Decode( char base64 ) {
	if( base64 >= 'A' && base64 <= 'Z' ) return base64 - 'A';
	else if( base64 >= 'a' && base64 <= 'z' ) return base64 - 'a' + 26;
	else if( base64 >= '0' && base64 <= '9' ) return base64 - '0' + 52;
	else if( base64 == '+' ) return 62;
	return 63;
}

bool XCBase64::IsBase64( char c) {
	return( (c>='A' && c<='Z') ||
			(c>='a' && c<='z') ||
			(c>='0' && c<='9') ||
			(c=='+') || (c=='/') || (c=='=') );
}

void XCBase64::Encode( char *base64, const unsigned char *buffer, int length ) {
	int i, j;
	unsigned char ToEncode8[3];
	unsigned char ToEncode6[4];
	bool second, third;

	for( i=0, j=0; i<length; i+=3 ) {
		second = (i+1 < length);		//do we have a second byte?
		third = (i+2 < length);			//do we have third byte?
		memset( ToEncode8, 0, 3 );		//pad the end so that we have three bytes
		ToEncode8[0] = buffer[i];
		if (second) ToEncode8[1] = buffer[i+1];
		if (third) ToEncode8[2] = buffer[i+2];

		memset( ToEncode6, 0, 4 );		//again pad the end with zeros
		ToEncode6[0] = (ToEncode8[0] >> 2);
		ToEncode6[1] = ((ToEncode8[0] & 0x3) << 4) | ((ToEncode8[1] >> 4) & 0xF);
		ToEncode6[2] = ((ToEncode8[1] & 0xF) << 2) | ((ToEncode8[2] >> 6) & 0x3);
		ToEncode6[3] = ToEncode8[2] & 0x3F;

		base64[j++] = Encode(ToEncode6[0]);
		base64[j++] = Encode(ToEncode6[1]);
		if (second) base64[j++] = Encode(ToEncode6[2]);
		else base64[j++] = '=';
		if (third) base64[j++] = Encode(ToEncode6[3]);
		else base64[j++] = '=';
	};

	base64[j] = '\0';

	return;
}

int XCBase64::Decode( const char *base64, unsigned char *buffer, int length ) {
	int i, j, k;
	unsigned char ToDecode6[4];

	for( i=0, j=0; i<length; i+=4 ) {
		// decode four characters at a time
		for( k=0;k<4;k++ ) {
			if( i+k < length)
				if( IsBase64(base64[i+k]) )
					ToDecode6[k] = Decode(base64[i+k]);
				else
					ToDecode6[k] = 0;
		};
		//and copy them over to the buffer
		buffer[j++] = ((ToDecode6[0] << 2) & 0xFC) | (ToDecode6[1] >> 4);
		if(base64[i+2] != '=') buffer[j++] = ((ToDecode6[1] << 4) & 0xF0) | ((ToDecode6[2] >> 2) & 0xF);
		if(base64[i+3] != '=') buffer[j++] = ((ToDecode6[2] << 6) & 0xC0) | (ToDecode6[3] & 0x3F);
	}

	return j;
}
